package tx.oopa.circuit.node;

import java.util.Vector;

import tx.oopa.circuit.port.AbstractComponent;

/**
 * The superclass which is used by all node implementations. The class itself implements the Observer pattern where it serves as
 * the observable. Next to that it contains a few getters and setters which are used by all nodes.
 * 
 * @author Xander van Rijn
 *
 */
public abstract class AbstractNode {

    private boolean changed = false;
    private Vector<NodeObserver> observers;
    
    protected AbstractComponent component;
    protected Boolean outputValue;
    private String name;

    public AbstractNode() {
        observers = new Vector<NodeObserver>();
    }

    /**
     * Used for adding an Observer.
     * 
     * @param o The Observer to add
     */
    protected synchronized void addObserver(NodeObserver o) {
        if (o == null)
            throw new NullPointerException();
        if (!observers.contains(o)) {
            observers.addElement(o);
        }
    }
    
    /**
     * Used for removing an Observer.
     * 
     * @param o The Observer to remove
     */
    protected synchronized void deleteObserver(NodeObserver o) {
        observers.removeElement(o);
    }

    /**
     * Used for notifying all observers.
     */
    protected void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * Used for notifying all observers with a given argument.
     * 
     * @param arg The extra argument.
     */
    protected void notifyObservers(Object arg) {
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = observers.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((NodeObserver)arrLocal[i]).update(this);
    }
    
    protected synchronized Vector<NodeObserver> getObservers() {
        return observers;
    }
    
    protected synchronized void deleteObservers() {
        observers.removeAllElements();
    }

    protected synchronized void setChanged() {
        changed = true;
    }
    
    protected synchronized void clearChanged() {
        changed = false;
    }

    protected synchronized boolean hasChanged() {
        return changed;
    }

    public synchronized int countObservers() {
        return observers.size();
    }
    
    /**
     * Set the component which is related to this node. This could either be a starting component, port or probe.
     * 
     * @param component The component to set in this node.
     */
    public void setComponent(AbstractComponent component) {
        this.component = component;
    }
    
    public AbstractComponent getComponent() {
        return this.component;
    }
    
    /**
     * Returns the output which is generated by calling the logical function of the component.
     * 
     * @return The output of the component.
     */
    public Boolean getOutput() {
        return this.outputValue;
    }

    /**
     * Set the name of the node.
     * 
     * @param name Name of the node
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Returns the name of the node.
     * 
     * @return The name of the node
     */
    public String getName() {
        return name;
    }
}
